/* autor Konjkov.VV@gmail.com */

#include <sstream>

#include "Database.h"
#include "Packet.h"

#include "common.h"

template <class T>
inline std::string toString(const T& t) {
std::stringstream ss;
ss<<t;
return ss.str();
};

OutPacket::OutPacket(std::string node) {
// Создание файла packet.info
    conf = Syncconf();
char packet_version[10] = "2.0";
    this->node = node;

    Database d;
    d.Connect(conf.dbsource);
// имя файла, в котором хранится информация
    packet_info.open((PATH_NODES + "/" + node + "/" + "packet.info").c_str(), std::ios::trunc);

    packet_info << "# === General packet description" << std::endl << std::endl;
    packet_info << "packet_security_level=" << conf.seclevel << std::endl;
// версия формата пакета
    packet_info << "packet_version=" << packet_version << std::endl;
// версия системы, создавшей пакет
    packet_info << "system_version=" << SYSTEM_VER << std::endl;
// от какого узла пакет
    packet_info << "packet_from=" << conf.nodename << std::endl;
// какому узлу предназначен пакет
    packet_info << "packet_to=" << node << std::endl;
// номер пакета
    Statement s1 = d.AllocStatement();
    s1.ExecDirect("SELECT current_packet FROM "+conf.schemaname+".syncMAIN");
    if (s1.Fetch()) {
        curpacket = s1.col[0]+1;
    }
    packet_info << "packet_number""=" << curpacket << std::endl;
// номер предыдущего пакета
    Statement s2 = d.AllocStatement();
    s2.ExecDirect("SELECT prepared FROM "+conf.schemaname+".syncNODES WHERE name = '"+node+"'");
    if (s2.Fetch()) {
        prevpacket == s2.col[0];
    }
    packet_info << "packet_prev=" << prevpacket << std::endl << std::endl;

    packet_info << "# === End general packet description" << std::endl << std::endl;
    packet_info << "# === Description tables" << std::endl << std::endl;
}

void OutPacket::addTable(std::string name, std::string fields, std::string condi, std::string alias) {

std::stringstream buf;

    Database d;
    d.Connect("nstat");

    std::string owner = name.substr(0,name.find("."));
    std::string table = name.substr(name.find(".")+1,name.length());

    packet_info <<  "# --- Description table " << name << std::endl << std::endl;

// список ключевых полей
    packet_info << "pkey_fields=";

    Statement s1 = d.AllocStatement();
    s1.PrimaryKeys(owner, table);
    while(s1.Fetch()){
        packet_info << s1.col[3] << ",";
    }
    packet_info << std::endl;

// Выгрузка данных из основной таблицы
    table_dat.open((PATH_NODES + "/" + node + "/" + owner+"_"+table+".dat").c_str(), std::ios::trunc);
    Statement s2 = d.AllocStatement();
// Фетчить по одной строке
    s2.SetAttr( SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER) 1);
    s2.ExecDirect("SELECT "+fields+" FROM "+name+" WHERE ("+SQL_SYSFIELD1+" = 0 OR "+SQL_SYSFIELD1+" > "+toString(prevpacket)+")"+condi);
    while(s2.Fetch()) {
        for (int i=0; i<s2.col.size(); i++) {
            table_dat << s2.col[i] << ",";
        };
        table_dat << std::endl;
    }
    table_dat.close();

// Обновление поля packet таблицы
//    d.AllocStatement().ExecDirect("UPDATE "+name+" SET "+SQL_SYSFIELD1+" = "+toString(curpacket)+" WHERE "+SQL_SYSFIELD1+" = 0 "+condi);

// Выгрузка данных из таблицы _del
    table_del.open((PATH_NODES + "/" + node + "/" + owner+"_"+table+".del").c_str(), std::ios::trunc);
    Statement s3 = d.AllocStatement();
// Фетчить по одной строке
    s3.SetAttr( SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER) 1);
    s3.ExecDirect("SELECT "+fields+" FROM "+name+"_"+SQL_PREFIX_DELTABLE+" WHERE ("+SQL_SYSFIELD1+" = 0 OR "+SQL_SYSFIELD1+" > "+toString(prevpacket)+") AND "+SQL_SYSFIELD2+" <= "+toString(prevpacket)+" "+condi);

    while(s3.Fetch()) {
        table_del << s3.col[0] << ",";
    }
    table_del.close();

// Обновление поля packet таблицы _del
//    d.AllocStatement().ExecDirect("UPDATE "+name+"_"+SQL_PREFIX_DELTABLE+" SET "+SQL_SYSFIELD1+" = "+toString(curpacket)+" WHERE "+SQL_SYSFIELD1+" = 0 "+condi);

// конец цикла

// список остальных полей
    packet_info << "other_fields=";
// описание всех полей, как
    packet_info << "create_clause=";

    packet_info <<  "# --- End description" << std::endl << std::endl;
};

OutPacket::~OutPacket() {
    packet_info << "# === End tables description" << std::endl;
    packet_info.close();
};


InPacket::InPacket(std::string node) {
};

void InPacket::loadTable(std::string name) {
}
/*
int tablesection_pkeyfields(FILE *packet_info, char* owner, char* table) {
// Получить список полей первичного ключа
struct field    primarykey;
struct field*   nextfield;
char            pkeyfields[MAX_NAME_LEN*64];
int             i=0;

    tableprimarykeys(owner, table, &primarykey);
    nextfield = &primarykey;
    while (nextfield != NULL) {
        i += sprintf(pkeyfields + i, "%s,", nextfield->name);
        nextfield = nextfield->next;
    }
    pkeyfields[i-1] = '\0';
    fprintf(packet_info, TABLE_SECTION_START, owner, table);
    fprintf(packet_info, PACKINFO_TABLE_PKEYS"='%s'\n", pkeyfields);
    return 0;
}

int tablesection_otherfields(FILE *packet_info, char* owner, char* table) {
// Получить список всех полей кроме полей первичного ключа
struct field    other;
struct field*   nextfield;
char            otherfields[MAX_NAME_LEN*64];
int             j=0;

    tableotherfields(owner, table, &other);
    nextfield = &other;
    while(nextfield != NULL) {
        j += sprintf(otherfields + j, "%s,", nextfield->name);
        nextfield = nextfield->next;
    }
    otherfields[j-1] = '\0';
    fprintf(packet_info, PACKINFO_TABLE_OTHER"='%s'\n", otherfields);
    return 0;
}

int tablesection_createclause(FILE *packet_info, SQLHSTMT* stmt) {
// Получить спиcок полей как в запросе CREATE TABLE
SQLRETURN     retcode;

int k=0;

SQLCHAR       ColumnName[MAX_NAME_LEN];
SQLLEN        ColumnSize;
SQLSMALLINT   DataType, DecimalDigits;

SQLSMALLINT   numcols;
int           col;

char          createclause[MAX_NAME_LEN*64];

    EXEC_SQL( SQLNumResultCols( stmt, &numcols) );
    for (col = 0; col < numcols; col++) {
        EXEC_SQL( SQLDescribeCol( stmt, col+1, ColumnName, MAX_NAME_LEN, NULL, &DataType, &ColumnSize, &DecimalDigits, NULL));
        if ( strcmp( ColumnName, "packet") && strcmp( ColumnName, "initpacket") ) {
            switch (DataType) {
            case SQL_GUID:
                k += sprintf(createclause + k, "%s varchar(%d),", ColumnName, ColumnSize);
                break;
            case SQL_BIT:
                k += sprintf(createclause + k, "%s bit,", ColumnName);
                break;
            case SQL_TINYINT:
                k += sprintf(createclause + k, "%s tinyint,", ColumnName);
                break;
            case SQL_BIGINT:
                k += sprintf(createclause + k, "%s bigint,", ColumnName);
                break;
            case SQL_LONGVARBINARY:
                k += sprintf(createclause + k, "%s longvarbinary,", ColumnName);
                break;
            case SQL_VARBINARY:
                k += sprintf(createclause + k, "%s varbinary,", ColumnName);
                break;
            case SQL_BINARY:
                k += sprintf(createclause + k, "%s binary,", ColumnName);
                break;
            case SQL_LONGVARCHAR:
                k += sprintf(createclause + k, "%s longvarchar(%d),", ColumnName, ColumnSize);
                break;
            case SQL_CHAR:
                k += sprintf(createclause + k, "%s char(%d),", ColumnName, ColumnSize);
                break;
            case SQL_NUMERIC:
                k += sprintf(createclause + k, "%s numeric(%d,%d),", ColumnName, ColumnSize, DecimalDigits);
                break;
            case SQL_DECIMAL:
                k += sprintf(createclause + k, "%s decimal(%d,%d),", ColumnName, ColumnSize, DecimalDigits);
                break;
            case SQL_INTEGER:
                k += sprintf(createclause + k, "%s integer,", ColumnName, ColumnSize);
                break;
            case SQL_SMALLINT:
                k += sprintf(createclause + k, "%s smallint,", ColumnName);
                break;
            case SQL_FLOAT:
                k += sprintf(createclause + k, "%s float,", ColumnName);
                break;
            case SQL_REAL:
                k += sprintf(createclause + k, "%s real,", ColumnName);
                break;
            case SQL_DOUBLE:
                k += sprintf(createclause + k, "%s double,", ColumnName);
                break;
            case SQL_TIME:
                k += sprintf(createclause + k, "%s time,", ColumnName);
                break;
            case SQL_TIMESTAMP:
                k += sprintf(createclause + k, "%s timestamp,", ColumnName);
                break;
            case SQL_DATETIME:
                k += sprintf(createclause + k, "%s datetime,", ColumnName);
                break;
            case SQL_VARCHAR:
                k += sprintf(createclause + k, "%s varchar(%d),", ColumnName, ColumnSize);
                break;
            default:
                k += sprintf(createclause + k, "%s %d", ColumnName, DataType);
                break;
            }
        }
    }
    createclause[k-1] = '\0';
    fprintf(packet_info, PACKINFO_TABLE_CREATE"='%s'\n\n", createclause);
    fprintf(packet_info, TABLE_SECTION_STOP);

    return 0;
}

int tableprimarykeys(char* owner, char* table, struct field* fields) {
// Получить список полей первичного ключа
SQLHSTMT      stmt;
SQLRETURN     retcode;

SQLCHAR       ColumnName[MAX_NAME_LEN];
SQLLEN        ColumnNameSize;

struct field* newfield = NULL;

    SQLAllocStmt( dbc, &stmt );
    EXEC_SQL( SQLPrimaryKeys( stmt, NULL, 0, owner, SQL_NTS, table, SQL_NTS) );
    SQLBindCol( stmt, 4, SQL_C_CHAR, ColumnName, MAX_NAME_LEN, &ColumnNameSize);
    while(1) {
        retcode = SQLFetch(stmt);
        if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
            if ( strcmp( ColumnName, "packet") && strcmp( ColumnName, "initpacket") ) {
                if (newfield == NULL) {
                    newfield = fields;
                } else {
                    newfield->next = malloc(sizeof(struct field));
                    newfield = newfield->next;
                }
                newfield->name = malloc(ColumnNameSize*sizeof(char)+1);
                strcpy(newfield->name, ColumnName);
                newfield->next=NULL;
            }
        } else if (retcode == SQL_NO_DATA){
            break;
        } else {
            return syncctl_error( stmt , __FILE__, __LINE__);
        }
    }
    SQLFreeStmt( stmt, SQL_CLOSE );
    return 0;
}

int tableallfields(char* owner, char* table, struct field* fields) {
// Получить список всех полей таблицы
SQLHSTMT      stmt;
SQLRETURN     retcode;

SQLCHAR       ColumnName[MAX_NAME_LEN];
SQLLEN        ColumnNameSize;

struct field* newfield = NULL;

    SQLAllocStmt( dbc, &stmt );
    EXEC_SQL( SQLColumns( stmt, NULL, 0, owner, SQL_NTS, table, SQL_NTS, NULL, 0) );
    SQLBindCol( stmt, 4, SQL_C_CHAR, ColumnName, MAX_NAME_LEN, &ColumnNameSize);
    while(1) {
        retcode = SQLFetch(stmt);
        if (retcode == SQL_SUCCESS || retcode == SQL_SUCCESS_WITH_INFO) {
            if ( strcmp( ColumnName, "packet") && strcmp( ColumnName, "initpacket") ) {
                if (newfield == NULL) {
                    newfield = fields;
                } else {
                    newfield->next = malloc(sizeof(struct field));
                    newfield = newfield->next;
                }
                newfield->name = malloc(ColumnNameSize*sizeof(char)+1);
                strcpy(newfield->name, ColumnName);
                newfield->next=NULL;
            }
        } else if (retcode == SQL_NO_DATA){
            break;
        } else {
            return syncctl_error( stmt , __FILE__, __LINE__);
        }
    }
    SQLFreeStmt( stmt, SQL_CLOSE );
    return 0;
}

int tableotherfields(char* owner, char* table, struct field* fields) {
// Получить список всех полей таблицы кроме ключевых
struct field primaryfields, allfields;
struct field *nextprimaryfield, *nextallfield;
struct field* newfield = NULL;
int          flag = 0;

    tableallfields(owner, table, &allfields);
    tableprimarykeys(owner, table, &primaryfields);
    nextallfield = &allfields;
    while (nextallfield != NULL) {
        nextprimaryfield = &primaryfields;
        flag = 0;
        while (nextprimaryfield != NULL) {
            if (!strcmp(nextprimaryfield->name, nextallfield->name)) {
                flag = 1;
                break;
            }
            nextprimaryfield = nextprimaryfield->next;
        }
        if (flag == 0) {
            if (newfield == NULL) {
                newfield = fields;
            } else {
                newfield->next = malloc(sizeof(struct field));
                newfield = newfield->next;
            }
            newfield->name = malloc(256*sizeof(char));
            strcpy(newfield->name, nextallfield->name);
            newfield->next=NULL;
        }
        nextallfield = nextallfield->next;
    }

    return 0;
}
*/
InPacket::~InPacket(){
};
